#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "ulog/ulog.h"
#include <aos/kernel.h>
#include "soc.h"
#include "drv_dmac.h"
#include "flash_indirect_opt.h"
#include "data_move.h"

static const char *TAG = "flash";
static volatile uint8_t _dma_cb_flag = 0;
static int _dma_ch = -1;

int flash_read_data(unsigned int offset, unsigned char *buf, unsigned int len);;

static void dma_event_cb_fun(int32_t ch, dma_event_e event, void *args)
{
    _dma_cb_flag = 1;
    
    LOGI(TAG," dma ch:%d done\n",ch);
    csi_dma_stop(_dma_ch);
}

int memcpy_dma_init()
{
    int ret = 0;
    if (_dma_ch >= 0)
        return ret;

    _dma_ch = csi_dma_alloc_channel();
    if (_dma_ch == -1) {
        printf("csi_dma_alloc_channel error\n");
        return -1;
    }

    dma_config_t config = {
        .src_inc  = DMA_ADDR_INC,
        .dst_inc  = DMA_ADDR_INC,
        .src_endian = DMA_ADDR_LITTLE,
        .dst_endian = DMA_ADDR_LITTLE,
        .src_tw   = 1,
        .dst_tw   = 1,
        .mode = DMA_BLOCK_TRIGGER,
        .type     = DMA_MEM2MEM,
        .ch_mode  = DMA_MODE_SOFTWARE,
        .group_len = 8,
    };
	void * args = NULL;
    ret = csi_dma_config_channel(_dma_ch, &config, dma_event_cb_fun, args);
    if (ret < 0) {
        printf("csi_dma_config_channel error\n");
        csi_dma_release_channel(_dma_ch);
        _dma_ch = -1;
    }

    return ret;
}

void memcpy_dma_release()
{
    if (_dma_ch < 0)
        return

    csi_dma_release_channel(_dma_ch);
    _dma_ch = -1;
}

int memcpy_dma(void *dst, void *src, size_t len)
{
    if (_dma_ch < 0) {
        printf("please call memcpy_dma_init() firistly\n");
        return -1;
    }
     LOGI(TAG, "dma ch %d start\n ", _dma_ch);
    _dma_cb_flag = 0;
    csi_dma_start(_dma_ch, src, dst, len);
    //while (!_dma_cb_flag);
    //csi_dma_stop(_dma_ch);

    return 0;
}
int  memcpy_dma_is_done(void)
{
    return _dma_cb_flag;
}
/*******************************************************************************************/

int memcpy_with_sleep(uint8_t* dst, uint8_t* src, uint32_t size) 
{
    uint32_t need_to_copy = size;
    uint32_t copy_size = 0;
    while(need_to_copy) {
        copy_size = need_to_copy > COPY_DATA_SIZE_MAX ?  COPY_DATA_SIZE_MAX : need_to_copy;
        indirect_memcpy(dst, src, copy_size);
        need_to_copy -= copy_size; 
        src += copy_size;
        dst += copy_size;
        if (copy_size >= COPY_DATA_SIZE_MAX) {
           //aos_msleep(1);
        }
    }
    return 0;
}

void print_data(char * addr, unsigned int size)
{
    int i;
    for(i = 0; i < size; i++ ) {
       printf("%x ", *(addr + i));
    }
    printf("\n");
}

typedef struct {
	spiflash_info_t spiflashinfo;
	spiflash_event_cb_t cb;
	spiflash_status_t status;
} ck_spiflash_priv_t;
/*****************************************************************/

#define DMA_SIZE_MAX 1024
uint8_t dst[DMA_SIZE_MAX]; 

void set_qspi_freq(void) 
{
    volatile uint32_t *base;
    uint32_t temp;
     /* switch qspi ref_clk to pll */
    base = (uint32_t *)(CSKY_CLKGEN_BASE + QSPI_CLK_CFG);//0xfff200e4
    /* qspi_rclk_switch_sel -> pll */
    *base &= 0xffffefff;

	base = (uint32_t *)(CSKY_QSPIC0_BASE + 0x10);//0xfff20010
	temp = *base;
	/* delay the read data capturing logic by the programmable number of ref_clk cycles */
    temp &= ~(0xF << 1);
	temp |= (0x4 << 1);
	*base = temp;

	/* set QSPI baud rate */
    base = (uint32_t *)(CSKY_QSPIC0_BASE + 0);
    temp = *base;
    /* set BD to 1, divisor as 4 */
    temp &= ~(0xF << 19);
	temp |= (0x1 << 19); //div = 4,clk =100MHz,ok
	//temp |= (0x8 << 19); //div = 18,clk =22.2MHz,ok
	//temp |= (0x6 << 19); //div = 14,clk =28.5MHz,ok
	//temp |= (0x5 << 19); //div = 12,clk =33.3MHz
	//temp |= (0x4 << 19); //div = 4,clk =40MHz
    *base = temp;
}


int qspi_flash_write(uint32_t offset, uint8_t *writeData, uint32_t size)
{
    uint32_t count = 0;
    uint32_t need_to_copy = size;
    uint32_t copy_size = 0;
    spiflash_handle_t flashHandle = NULL;

    printf("size %d \n", size);

    flashHandle = csi_spiflash_initialize(0, NULL);
    while(need_to_copy) {
        copy_size = need_to_copy > WRITE_DATA_SIZE_MAX ?  WRITE_DATA_SIZE_MAX : need_to_copy;
        
        csi_spiflash_erase_sector(flashHandle, offset+count);    //erase
        csi_spiflash_program(flashHandle, offset+count, (uint8_t *)writeData+count, copy_size);
        need_to_copy -= copy_size; 
        count += WRITE_DATA_SIZE_MAX;
    }

    csi_spiflash_uninitialize(flashHandle);

    return 0;
}

static int32_t get_img_info(uint32_t store_addr, uint32_t *load_addr, uint32_t *img_len)
{
    img_head_t img_head;

    /* get img load info */
    if(flash_read_data(store_addr, (uint8_t *)&img_head, sizeof(img_head_t)) != SUCCESS){
		printf("read img head err\n");
		return FAILURE;
    }
    if(MAGICNUM != img_head.magic){
		printf("img maigc num err\n");
		return FAILURE;
    }
	
    *load_addr = img_head.load_addr;
    *img_len = img_head.img_len;
	
    return SUCCESS;
}


int32_t copy_img(store_mem_t *store_mem, uint32_t boot_base, uint32_t *target_addr)
{
    int32_t ret;
    uint32_t load_addr;
    uint32_t img_len;
	
    ret = get_img_info(boot_base + store_mem->store_off, &load_addr, &img_len);
    if(SUCCESS != ret){
		printf("get img info faild\n");
		return FAILURE;
    }
	
    if((load_addr != CK805_ITCM_START) && (load_addr != CK805_DTCM_START) && (load_addr > DDR_END || load_addr < DDR_START)){
		printf("load addr is invalid %x \n", load_addr);
        return FAILURE;
    }
	
//	if (NULL != target_addr)
//		*target_addr = load_addr;

    if(img_len > store_mem->mem_size){
		printf("img_len is invalid %x \n", img_len);
        return FAILURE;
    }
	
    if(flash_read_data(boot_base + store_mem->store_off + sizeof(img_head_t), (uint8_t *)load_addr, img_len) != SUCCESS){
		printf("load img err, load addr is %x, store addr is %x, img_len is %x \n", load_addr, boot_base + store_mem->store_off, img_len);
        return FAILURE;
    }
	
    return SUCCESS;
}

